1/* Plugin for offload execution on Intel MIC devices.
2
3   Copyright (C) 2014 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/* Target side part of a libgomp plugin.  */
30
31#include <stdint.h>
32#include <stdio.h>
33#include <stdlib.h>
34#include "compiler_if_target.h"
35
36
37#ifdef DEBUG
38#define TRACE(...)					      \
39{							      \
40fprintf (stderr, "TARGET:\t%s:%s ", __FILE__, __FUNCTION__);  \
41fprintf (stderr, __VA_ARGS__);				      \
42fprintf (stderr, "\n");					      \
43}
44#else
45#define TRACE { }
46#endif
47
48
49static VarDesc vd_host2tgt = {
50  { 1, 1 },		      /* dst, src			      */
51  { 1, 0 },		      /* in, out			      */
52  1,			      /* alloc_if			      */
53  1,			      /* free_if			      */
54  4,			      /* align				      */
55  0,			      /* mic_offset			      */
56  { 0, 0, 0, 0, 0, 0, 0, 0 }, /* is_static, is_static_dstn, has_length,
57				 is_stack_buf, sink_addr, alloc_disp,
58				 is_noncont_src, is_noncont_dst	      */
59  0,			      /* offset				      */
60  0,			      /* size				      */
61  1,			      /* count				      */
62  0,			      /* alloc				      */
63  0,			      /* into				      */
64  0			      /* ptr				      */
65};
66
67static VarDesc vd_tgt2host = {
68  { 1, 1 },		      /* dst, src			      */
69  { 0, 1 },		      /* in, out			      */
70  1,			      /* alloc_if			      */
71  1,			      /* free_if			      */
72  4,			      /* align				      */
73  0,			      /* mic_offset			      */
74  { 0, 0, 0, 0, 0, 0, 0, 0 }, /* is_static, is_static_dstn, has_length,
75				 is_stack_buf, sink_addr, alloc_disp,
76				 is_noncont_src, is_noncont_dst	      */
77  0,			      /* offset				      */
78  0,			      /* size				      */
79  1,			      /* count				      */
80  0,			      /* alloc				      */
81  0,			      /* into				      */
82  0			      /* ptr				      */
83};
84
85/* Pointer to the descriptor of the last loaded shared library.  */
86static void *last_loaded_library = NULL;
87
88/* Pointer and size of the variable, used in __offload_target_host2tgt_p[12]
89   and __offload_target_tgt2host_p[12].  */
90static void *last_var_ptr = NULL;
91static int last_var_size = 0;
92
93
94/* Override the corresponding functions from libgomp.  */
95extern "C" int
96omp_is_initial_device (void) __GOMP_NOTHROW
97{
98  return 0;
99}
100
101extern "C" int32_t
102omp_is_initial_device_ (void)
103{
104  return omp_is_initial_device ();
105}
106
107
108/* Dummy function needed for the initialization of target process during the
109   first call to __offload_offload1.  */
110static void
111__offload_target_init_proc (OFFLOAD ofldt)
112{
113  TRACE ("");
114}
115
116/* Collect addresses of the offload functions and of the global variables from
117   the library descriptor and send them to host.
118   Part 1: Send num_funcs and num_vars to host.  */
119static void
120__offload_target_table_p1 (OFFLOAD ofldt)
121{
122  void ***lib_descr = (void ***) last_loaded_library;
123
124  if (lib_descr == NULL)
125    {
126      TRACE ("");
127      fprintf (stderr, "Error! No shared libraries loaded on target.\n");
128      return;
129    }
130
131  void **func_table_begin = lib_descr[0];
132  void **func_table_end   = lib_descr[1];
133  void **var_table_begin  = lib_descr[2];
134  void **var_table_end    = lib_descr[3];
135
136  /* The func table contains only addresses, the var table contains addresses
137     and corresponding sizes.  */
138  int num_funcs = func_table_end - func_table_begin;
139  int num_vars = (var_table_end - var_table_begin) / 2;
140  TRACE ("(num_funcs = %d, num_vars = %d)", num_funcs, num_vars);
141
142  VarDesc vd1[2] = { vd_tgt2host, vd_tgt2host };
143  vd1[0].ptr = &num_funcs;
144  vd1[0].size = sizeof (num_funcs);
145  vd1[1].ptr = &num_vars;
146  vd1[1].size = sizeof (num_vars);
147  VarDesc2 vd2[2] = { { "num_funcs", 0 }, { "num_vars", 0 } };
148
149  __offload_target_enter (ofldt, 2, vd1, vd2);
150  __offload_target_leave (ofldt);
151}
152
153/* Part 2: Send the table with addresses to host.  */
154static void
155__offload_target_table_p2 (OFFLOAD ofldt)
156{
157  void ***lib_descr = (void ***) last_loaded_library;
158  void **func_table_begin = lib_descr[0];
159  void **func_table_end   = lib_descr[1];
160  void **var_table_begin  = lib_descr[2];
161  void **var_table_end    = lib_descr[3];
162
163  int num_funcs = func_table_end - func_table_begin;
164  int num_vars = (var_table_end - var_table_begin) / 2;
165  int table_size = (num_funcs + 2 * num_vars) * sizeof (void *);
166  void **table = (void **) malloc (table_size);
167  TRACE ("(table_size = %d)", table_size);
168
169  VarDesc vd1;
170  vd1 = vd_tgt2host;
171  vd1.ptr = table;
172  vd1.size = table_size;
173  VarDesc2 vd2 = { "table", 0 };
174
175  __offload_target_enter (ofldt, 1, &vd1, &vd2);
176
177  void **p;
178  int i = 0;
179  for (p = func_table_begin; p < func_table_end; p++, i++)
180    table[i] = *p;
181
182  for (p = var_table_begin; p < var_table_end; p++, i++)
183    table[i] = *p;
184
185  __offload_target_leave (ofldt);
186  free (table);
187}
188
189/* Allocate size bytes and send a pointer to the allocated memory to host.  */
190static void
191__offload_target_alloc (OFFLOAD ofldt)
192{
193  size_t size = 0;
194  void *ptr = NULL;
195
196  VarDesc vd1[2] = { vd_host2tgt, vd_tgt2host };
197  vd1[0].ptr = &size;
198  vd1[0].size = sizeof (size);
199  vd1[1].ptr = &ptr;
200  vd1[1].size = sizeof (void *);
201  VarDesc2 vd2[2] = { { "size", 0 }, { "ptr", 0 } };
202
203  __offload_target_enter (ofldt, 2, vd1, vd2);
204  ptr = malloc (size);
205  TRACE ("(size = %d): ptr = %p", size, ptr);
206  __offload_target_leave (ofldt);
207}
208
209/* Free the memory space pointed to by ptr.  */
210static void
211__offload_target_free (OFFLOAD ofldt)
212{
213  void *ptr = 0;
214
215  VarDesc vd1 = vd_host2tgt;
216  vd1.ptr = &ptr;
217  vd1.size = sizeof (void *);
218  VarDesc2 vd2 = { "ptr", 0 };
219
220  __offload_target_enter (ofldt, 1, &vd1, &vd2);
221  TRACE ("(ptr = %p)", ptr);
222  free (ptr);
223  __offload_target_leave (ofldt);
224}
225
226/* Receive var_size bytes from host and store to var_ptr.
227   Part 1: Receive var_ptr and var_size from host.  */
228static void
229__offload_target_host2tgt_p1 (OFFLOAD ofldt)
230{
231  void *var_ptr = NULL;
232  size_t var_size = 0;
233
234  VarDesc vd1[2] = { vd_host2tgt, vd_host2tgt };
235  vd1[0].ptr = &var_ptr;
236  vd1[0].size = sizeof (void *);
237  vd1[1].ptr = &var_size;
238  vd1[1].size = sizeof (var_size);
239  VarDesc2 vd2[2] = { { "var_ptr", 0 }, { "var_size", 0 } };
240
241  __offload_target_enter (ofldt, 2, vd1, vd2);
242  TRACE ("(var_ptr = %p, var_size = %d)", var_ptr, var_size);
243  last_var_ptr = var_ptr;
244  last_var_size = var_size;
245  __offload_target_leave (ofldt);
246}
247
248/* Part 2: Receive the data from host.  */
249static void
250__offload_target_host2tgt_p2 (OFFLOAD ofldt)
251{
252  TRACE ("(last_var_ptr = %p, last_var_size = %d)",
253	 last_var_ptr, last_var_size);
254
255  VarDesc vd1 = vd_host2tgt;
256  vd1.ptr = last_var_ptr;
257  vd1.size = last_var_size;
258  VarDesc2 vd2 = { "var", 0 };
259
260  __offload_target_enter (ofldt, 1, &vd1, &vd2);
261  __offload_target_leave (ofldt);
262}
263
264/* Send var_size bytes from var_ptr to host.
265   Part 1: Receive var_ptr and var_size from host.  */
266static void
267__offload_target_tgt2host_p1 (OFFLOAD ofldt)
268{
269  void *var_ptr = NULL;
270  size_t var_size = 0;
271
272  VarDesc vd1[2] = { vd_host2tgt, vd_host2tgt };
273  vd1[0].ptr = &var_ptr;
274  vd1[0].size = sizeof (void *);
275  vd1[1].ptr = &var_size;
276  vd1[1].size = sizeof (var_size);
277  VarDesc2 vd2[2] = { { "var_ptr", 0 }, { "var_size", 0 } };
278
279  __offload_target_enter (ofldt, 2, vd1, vd2);
280  TRACE ("(var_ptr = %p, var_size = %d)", var_ptr, var_size);
281  last_var_ptr = var_ptr;
282  last_var_size = var_size;
283  __offload_target_leave (ofldt);
284}
285
286/* Part 2: Send the data to host.  */
287static void
288__offload_target_tgt2host_p2 (OFFLOAD ofldt)
289{
290  TRACE ("(last_var_ptr = %p, last_var_size = %d)",
291	 last_var_ptr, last_var_size);
292
293  VarDesc vd1 = vd_tgt2host;
294  vd1.ptr = last_var_ptr;
295  vd1.size = last_var_size;
296  VarDesc2 vd2 = { "var", 0 };
297
298  __offload_target_enter (ofldt, 1, &vd1, &vd2);
299  __offload_target_leave (ofldt);
300}
301
302/* Call offload function by the address fn_ptr and pass vars_ptr to it.  */
303static void
304__offload_target_run (OFFLOAD ofldt)
305{
306  void *fn_ptr;
307  void *vars_ptr;
308
309  VarDesc vd1[2] = { vd_host2tgt, vd_host2tgt };
310  vd1[0].ptr = &fn_ptr;
311  vd1[0].size = sizeof (void *);
312  vd1[1].ptr = &vars_ptr;
313  vd1[1].size = sizeof (void *);
314  VarDesc2 vd2[2] = { { "fn_ptr", 0 }, { "vars_ptr", 0 } };
315
316  __offload_target_enter (ofldt, 2, vd1, vd2);
317  TRACE ("(fn_ptr = %p, vars_ptr = %p)", fn_ptr, vars_ptr);
318  void (*fn)(void *) = (void (*)(void *)) fn_ptr;
319  fn (vars_ptr);
320  __offload_target_leave (ofldt);
321}
322
323
324/* This should be called from every library with offloading.  */
325extern "C" void
326target_register_lib (const void *target_table)
327{
328  TRACE ("(target_table = %p { %p, %p, %p, %p })", target_table,
329	 ((void **) target_table)[0], ((void **) target_table)[1],
330	 ((void **) target_table)[2], ((void **) target_table)[3]);
331
332  last_loaded_library = (void *) target_table;
333}
334
335/* Use __offload_target_main from liboffload.  */
336int
337main (int argc, char **argv)
338{
339  __offload_target_main ();
340  return 0;
341}
342
343
344/* Register offload_target_main's functions in the liboffload.  */
345
346struct Entry {
347  const char *name;
348  void *func;
349};
350
351#define REGISTER(f)				      \
352extern "C" const Entry __offload_target_##f##_$entry  \
353__attribute__ ((section(".OffloadEntryTable."))) = {  \
354  "__offload_target_"#f,			      \
355  (void *) __offload_target_##f			      \
356}
357REGISTER (init_proc);
358REGISTER (table_p1);
359REGISTER (table_p2);
360REGISTER (alloc);
361REGISTER (free);
362REGISTER (host2tgt_p1);
363REGISTER (host2tgt_p2);
364REGISTER (tgt2host_p1);
365REGISTER (tgt2host_p2);
366REGISTER (run);
367#undef REGISTER
368