]> git.ipfire.org Git - thirdparty/gcc.git/blob - liboffloadmic/plugin/libgomp-plugin-intelmic.cpp
Add OpenACC 2.6 `acc_get_property' support
[thirdparty/gcc.git] / liboffloadmic / plugin / libgomp-plugin-intelmic.cpp
1 /* Plugin for offload execution on Intel MIC devices.
2
3 Copyright (C) 2014-2016 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 OFFLOAD_ACTIVE_WAIT_ENV "OFFLOAD_ACTIVE_WAIT"
44
45 #ifdef DEBUG
46 #define TRACE(...) \
47 { \
48 fprintf (stderr, "HOST:\t%s:%s ", __FILE__, __FUNCTION__); \
49 fprintf (stderr, __VA_ARGS__); \
50 fprintf (stderr, "\n"); \
51 }
52 #else
53 #define TRACE { }
54 #endif
55
56
57 /* Start/end addresses of functions and global variables on a device. */
58 typedef std::vector<addr_pair> AddrVect;
59
60 /* Addresses for one image and all devices. */
61 typedef std::vector<AddrVect> DevAddrVect;
62
63 /* Addresses for all images and all devices. */
64 typedef std::map<const void *, DevAddrVect> ImgDevAddrMap;
65
66 /* Image descriptor needed by __offload_[un]register_image. */
67 struct TargetImageDesc {
68 int64_t size;
69 /* 10 characters is enough for max int value. */
70 char name[sizeof ("lib0000000000.so")];
71 char data[];
72 };
73
74 /* Image descriptors, indexed by a pointer obtained from libgomp. */
75 typedef std::map<const void *, TargetImageDesc *> ImgDescMap;
76
77
78 /* Total number of available devices. */
79 static int num_devices;
80
81 /* Total number of shared libraries with offloading to Intel MIC. */
82 static int num_images;
83
84 /* Two dimensional array: one key is a pointer to image,
85 second key is number of device. Contains a vector of pointer pairs. */
86 static ImgDevAddrMap *address_table;
87
88 /* Descriptors of all images, registered in liboffloadmic. */
89 static ImgDescMap *image_descriptors;
90
91 /* Thread-safe registration of the main image. */
92 static pthread_once_t main_image_is_registered = PTHREAD_ONCE_INIT;
93
94 static VarDesc vd_host2tgt = {
95 { 1, 1 }, /* dst, src */
96 { 1, 0 }, /* in, out */
97 1, /* alloc_if */
98 1, /* free_if */
99 4, /* align */
100 0, /* mic_offset */
101 { 0, 0, 0, 0, 0, 0, 0, 0 }, /* is_static, is_static_dstn, has_length,
102 is_stack_buf, sink_addr, alloc_disp,
103 is_noncont_src, is_noncont_dst */
104 0, /* offset */
105 0, /* size */
106 1, /* count */
107 0, /* alloc */
108 0, /* into */
109 0 /* ptr */
110 };
111
112 static VarDesc vd_tgt2host = {
113 { 1, 1 }, /* dst, src */
114 { 0, 1 }, /* in, out */
115 1, /* alloc_if */
116 1, /* free_if */
117 4, /* align */
118 0, /* mic_offset */
119 { 0, 0, 0, 0, 0, 0, 0, 0 }, /* is_static, is_static_dstn, has_length,
120 is_stack_buf, sink_addr, alloc_disp,
121 is_noncont_src, is_noncont_dst */
122 0, /* offset */
123 0, /* size */
124 1, /* count */
125 0, /* alloc */
126 0, /* into */
127 0 /* ptr */
128 };
129
130
131 __attribute__((constructor))
132 static void
133 init (void)
134 {
135 const char *active_wait = getenv (OFFLOAD_ACTIVE_WAIT_ENV);
136
137 /* Disable active wait by default to avoid useless CPU usage. */
138 if (!active_wait)
139 setenv (OFFLOAD_ACTIVE_WAIT_ENV, "0", 0);
140
141 address_table = new ImgDevAddrMap;
142 image_descriptors = new ImgDescMap;
143 num_devices = _Offload_number_of_devices ();
144 }
145
146 extern "C" const char *
147 GOMP_OFFLOAD_get_name (void)
148 {
149 const char *res = "intelmic";
150 TRACE ("(): return %s", res);
151 return res;
152 }
153
154 extern "C" unsigned int
155 GOMP_OFFLOAD_get_caps (void)
156 {
157 unsigned int res = GOMP_OFFLOAD_CAP_OPENMP_400;
158 TRACE ("(): return %x", res);
159 return res;
160 }
161
162 extern "C" int
163 GOMP_OFFLOAD_get_type (void)
164 {
165 enum offload_target_type res = OFFLOAD_TARGET_TYPE_INTEL_MIC;
166 TRACE ("(): return %d", res);
167 return res;
168 }
169
170 extern "C" int
171 GOMP_OFFLOAD_get_num_devices (void)
172 {
173 TRACE ("(): return %d", num_devices);
174 return num_devices;
175 }
176
177 extern "C" union gomp_device_property_value
178 GOMP_OFFLOAD_get_property (int n, int prop)
179 {
180 union gomp_device_property_value nullval = { .val = 0 };
181
182 if (n >= num_devices)
183 {
184 GOMP_PLUGIN_error
185 ("Request for a property of a non-existing Intel MIC device %i", n);
186 return nullval;
187 }
188
189 switch (prop)
190 {
191 case GOMP_DEVICE_PROPERTY_VENDOR:
192 return (union gomp_device_property_value) { .ptr = "Intel" };
193 default:
194 return nullval;
195 }
196 }
197
198 static bool
199 offload (const char *file, uint64_t line, int device, const char *name,
200 int num_vars, VarDesc *vars, const void **async_data)
201 {
202 OFFLOAD ofld = __offload_target_acquire1 (&device, file, line);
203 if (ofld)
204 {
205 if (async_data == NULL)
206 return __offload_offload1 (ofld, name, 0, num_vars, vars, NULL, 0,
207 NULL, NULL);
208 else
209 {
210 OffloadFlags flags;
211 flags.flags = 0;
212 flags.bits.omp_async = 1;
213 return __offload_offload3 (ofld, name, 0, num_vars, vars, NULL, 0,
214 NULL, async_data, 0, NULL, flags, NULL);
215 }
216 }
217 else
218 {
219 GOMP_PLUGIN_error ("%s:%d: Offload target acquire failed\n", file, line);
220 return false;
221 }
222 }
223
224 static void
225 register_main_image ()
226 {
227 /* Do not check the return value, because old versions of liboffloadmic did
228 not have return values. */
229 __offload_register_image (&main_target_image);
230
231 /* liboffloadmic will call GOMP_PLUGIN_target_task_completion when
232 asynchronous task on target is completed. */
233 __offload_register_task_callback (GOMP_PLUGIN_target_task_completion);
234 }
235
236 /* liboffloadmic loads and runs offload_target_main on all available devices
237 during a first call to offload (). */
238 extern "C" bool
239 GOMP_OFFLOAD_init_device (int device)
240 {
241 TRACE ("(device = %d)", device);
242 pthread_once (&main_image_is_registered, register_main_image);
243 return offload (__FILE__, __LINE__, device, "__offload_target_init_proc", 0,
244 NULL, NULL);
245 }
246
247 extern "C" bool
248 GOMP_OFFLOAD_fini_device (int device)
249 {
250 TRACE ("(device = %d)", device);
251
252 /* liboffloadmic will finalize target processes on all available devices. */
253 __offload_unregister_image (&main_target_image);
254 return true;
255 }
256
257 static bool
258 get_target_table (int device, int &num_funcs, int &num_vars, void **&table)
259 {
260 VarDesc vd1[2] = { vd_tgt2host, vd_tgt2host };
261 vd1[0].ptr = &num_funcs;
262 vd1[0].size = sizeof (num_funcs);
263 vd1[1].ptr = &num_vars;
264 vd1[1].size = sizeof (num_vars);
265
266 if (!offload (__FILE__, __LINE__, device, "__offload_target_table_p1", 2,
267 vd1, NULL))
268 return false;
269
270 int table_size = num_funcs + 2 * num_vars;
271 if (table_size > 0)
272 {
273 table = new void * [table_size];
274
275 VarDesc vd2;
276 vd2 = vd_tgt2host;
277 vd2.ptr = table;
278 vd2.size = table_size * sizeof (void *);
279
280 return offload (__FILE__, __LINE__, device, "__offload_target_table_p2",
281 1, &vd2, NULL);
282 }
283 return true;
284 }
285
286 /* Offload TARGET_IMAGE to all available devices and fill address_table with
287 corresponding target addresses. */
288
289 static bool
290 offload_image (const void *target_image)
291 {
292 void *image_start = ((void **) target_image)[0];
293 void *image_end = ((void **) target_image)[1];
294
295 TRACE ("(target_image = %p { %p, %p })",
296 target_image, image_start, image_end);
297
298 int64_t image_size = (uintptr_t) image_end - (uintptr_t) image_start;
299 TargetImageDesc *image = (TargetImageDesc *) malloc (offsetof (TargetImageDesc, data)
300 + image_size);
301 if (!image)
302 {
303 GOMP_PLUGIN_error ("%s: Can't allocate memory\n", __FILE__);
304 return false;
305 }
306
307 image->size = image_size;
308 sprintf (image->name, "lib%010d.so", num_images++);
309 memcpy (image->data, image_start, image->size);
310
311 TRACE ("() __offload_register_image %s { %p, %d }",
312 image->name, image_start, image->size);
313 /* Do not check the return value, because old versions of liboffloadmic did
314 not have return values. */
315 __offload_register_image (image);
316
317 /* Receive tables for target_image from all devices. */
318 DevAddrVect dev_table;
319 bool ret = true;
320 for (int dev = 0; dev < num_devices; dev++)
321 {
322 int num_funcs = 0;
323 int num_vars = 0;
324 void **table = NULL;
325
326 ret &= get_target_table (dev, num_funcs, num_vars, table);
327
328 AddrVect curr_dev_table;
329
330 for (int i = 0; i < num_funcs; i++)
331 {
332 addr_pair tgt_addr;
333 tgt_addr.start = (uintptr_t) table[i];
334 tgt_addr.end = tgt_addr.start + 1;
335 TRACE ("() func %d:\t0x%llx..0x%llx", i,
336 tgt_addr.start, tgt_addr.end);
337 curr_dev_table.push_back (tgt_addr);
338 }
339
340 for (int i = 0; i < num_vars; i++)
341 {
342 addr_pair tgt_addr;
343 tgt_addr.start = (uintptr_t) table[num_funcs+i*2];
344 tgt_addr.end = tgt_addr.start + (uintptr_t) table[num_funcs+i*2+1];
345 TRACE ("() var %d:\t0x%llx..0x%llx", i, tgt_addr.start, tgt_addr.end);
346 curr_dev_table.push_back (tgt_addr);
347 }
348
349 dev_table.push_back (curr_dev_table);
350 delete [] table;
351 }
352
353 address_table->insert (std::make_pair (target_image, dev_table));
354 image_descriptors->insert (std::make_pair (target_image, image));
355 return ret;
356 }
357
358 /* Return the libgomp version number we're compatible with. There is
359 no requirement for cross-version compatibility. */
360
361 extern "C" unsigned
362 GOMP_OFFLOAD_version (void)
363 {
364 return GOMP_VERSION;
365 }
366
367 extern "C" int
368 GOMP_OFFLOAD_load_image (int device, const unsigned version,
369 const void *target_image, addr_pair **result)
370 {
371 TRACE ("(device = %d, target_image = %p)", device, target_image);
372
373 if (GOMP_VERSION_DEV (version) > GOMP_VERSION_INTEL_MIC)
374 {
375 GOMP_PLUGIN_error ("Offload data incompatible with intelmic plugin"
376 " (expected %u, received %u)",
377 GOMP_VERSION_INTEL_MIC, GOMP_VERSION_DEV (version));
378 return -1;
379 }
380
381 /* If target_image is already present in address_table, then there is no need
382 to offload it. */
383 if (address_table->count (target_image) == 0)
384 {
385 /* If fail, return -1 as error code. */
386 if (!offload_image (target_image))
387 return -1;
388 }
389
390 AddrVect *curr_dev_table = &(*address_table)[target_image][device];
391 int table_size = curr_dev_table->size ();
392 addr_pair *table = (addr_pair *) malloc (table_size * sizeof (addr_pair));
393 if (table == NULL)
394 {
395 GOMP_PLUGIN_error ("%s: Can't allocate memory\n", __FILE__);
396 return -1;
397 }
398
399 std::copy (curr_dev_table->begin (), curr_dev_table->end (), table);
400 *result = table;
401 return table_size;
402 }
403
404 extern "C" bool
405 GOMP_OFFLOAD_unload_image (int device, unsigned version,
406 const void *target_image)
407 {
408 if (GOMP_VERSION_DEV (version) > GOMP_VERSION_INTEL_MIC)
409 {
410 GOMP_PLUGIN_error ("Offload data incompatible with intelmic plugin"
411 " (expected %u, received %u)",
412 GOMP_VERSION_INTEL_MIC, GOMP_VERSION_DEV (version));
413 return false;
414 }
415
416 TRACE ("(device = %d, target_image = %p)", device, target_image);
417
418 /* liboffloadmic unloads the image from all available devices. */
419 if (image_descriptors->count (target_image) > 0)
420 {
421 TargetImageDesc *image_desc = (*image_descriptors)[target_image];
422 __offload_unregister_image (image_desc);
423 free (image_desc);
424
425 address_table->erase (target_image);
426 image_descriptors->erase (target_image);
427 }
428 return true;
429 }
430
431 extern "C" void *
432 GOMP_OFFLOAD_alloc (int device, size_t size)
433 {
434 TRACE ("(device = %d, size = %d)", device, size);
435
436 void *tgt_ptr;
437 VarDesc vd[2] = { vd_host2tgt, vd_tgt2host };
438 vd[0].ptr = &size;
439 vd[0].size = sizeof (size);
440 vd[1].ptr = &tgt_ptr;
441 vd[1].size = sizeof (void *);
442
443 if (!offload (__FILE__, __LINE__, device, "__offload_target_alloc", 2,
444 vd, NULL))
445 return NULL;
446
447 return tgt_ptr;
448 }
449
450 extern "C" bool
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 return offload (__FILE__, __LINE__, device, "__offload_target_free", 1,
460 &vd, NULL);
461 }
462
463 extern "C" bool
464 GOMP_OFFLOAD_host2dev (int device, void *tgt_ptr, const void *host_ptr,
465 size_t size)
466 {
467 TRACE ("(device = %d, tgt_ptr = %p, host_ptr = %p, size = %d)",
468 device, tgt_ptr, host_ptr, size);
469 if (!size)
470 return true;
471
472 VarDesc vd1[2] = { vd_host2tgt, vd_host2tgt };
473 vd1[0].ptr = &tgt_ptr;
474 vd1[0].size = sizeof (void *);
475 vd1[1].ptr = &size;
476 vd1[1].size = sizeof (size);
477
478 if (!offload (__FILE__, __LINE__, device, "__offload_target_host2tgt_p1", 2,
479 vd1, NULL))
480 return false;
481
482 VarDesc vd2 = vd_host2tgt;
483 vd2.ptr = (void *) host_ptr;
484 vd2.size = size;
485
486 return offload (__FILE__, __LINE__, device, "__offload_target_host2tgt_p2", 1,
487 &vd2, NULL);
488 }
489
490 extern "C" bool
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 true;
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 if (!offload (__FILE__, __LINE__, device, "__offload_target_tgt2host_p1", 2,
506 vd1, NULL))
507 return false;
508
509 VarDesc vd2 = vd_tgt2host;
510 vd2.ptr = (void *) host_ptr;
511 vd2.size = size;
512
513 return offload (__FILE__, __LINE__, device, "__offload_target_tgt2host_p2", 1,
514 &vd2, NULL);
515 }
516
517 extern "C" bool
518 GOMP_OFFLOAD_dev2dev (int device, void *dst_ptr, const void *src_ptr,
519 size_t size)
520 {
521 TRACE ("(device = %d, dst_ptr = %p, src_ptr = %p, size = %d)",
522 device, dst_ptr, src_ptr, size);
523 if (!size)
524 return true;
525
526 VarDesc vd[3] = { vd_host2tgt, vd_host2tgt, vd_host2tgt };
527 vd[0].ptr = &dst_ptr;
528 vd[0].size = sizeof (void *);
529 vd[1].ptr = &src_ptr;
530 vd[1].size = sizeof (void *);
531 vd[2].ptr = &size;
532 vd[2].size = sizeof (size);
533
534 return offload (__FILE__, __LINE__, device, "__offload_target_tgt2tgt", 3,
535 vd, NULL);
536 }
537
538 extern "C" void
539 GOMP_OFFLOAD_async_run (int device, void *tgt_fn, void *tgt_vars,
540 void **, void *async_data)
541 {
542 TRACE ("(device = %d, tgt_fn = %p, tgt_vars = %p, async_data = %p)", device,
543 tgt_fn, tgt_vars, async_data);
544
545 VarDesc vd[2] = { vd_host2tgt, vd_host2tgt };
546 vd[0].ptr = &tgt_fn;
547 vd[0].size = sizeof (void *);
548 vd[1].ptr = &tgt_vars;
549 vd[1].size = sizeof (void *);
550
551 offload (__FILE__, __LINE__, device, "__offload_target_run", 2, vd,
552 (const void **) async_data);
553 }
554
555 extern "C" void
556 GOMP_OFFLOAD_run (int device, void *tgt_fn, void *tgt_vars, void **)
557 {
558 TRACE ("(device = %d, tgt_fn = %p, tgt_vars = %p)", device, tgt_fn, tgt_vars);
559
560 GOMP_OFFLOAD_async_run (device, tgt_fn, tgt_vars, NULL, NULL);
561 }