|  | /* Plugin for offload execution on Intel MIC devices. | 
|  |  | 
|  | Copyright (C) 2014-2016 Free Software Foundation, Inc. | 
|  |  | 
|  | Contributed by Ilya Verbin <ilya.verbin@intel.com>. | 
|  |  | 
|  | This file is part of the GNU Offloading and Multi Processing Library | 
|  | (libgomp). | 
|  |  | 
|  | Libgomp is free software; you can redistribute it and/or modify it | 
|  | under the terms of the GNU General Public License as published by | 
|  | the Free Software Foundation; either version 3, or (at your option) | 
|  | any later version. | 
|  |  | 
|  | Libgomp is distributed in the hope that it will be useful, but WITHOUT ANY | 
|  | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | 
|  | FOR A PARTICULAR PURPOSE.  See the GNU General Public License for | 
|  | more details. | 
|  |  | 
|  | Under Section 7 of GPL version 3, you are granted additional | 
|  | permissions described in the GCC Runtime Library Exception, version | 
|  | 3.1, as published by the Free Software Foundation. | 
|  |  | 
|  | You should have received a copy of the GNU General Public License and | 
|  | a copy of the GCC Runtime Library Exception along with this program; | 
|  | see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see | 
|  | <http://www.gnu.org/licenses/>.  */ | 
|  |  | 
|  | /* Target side part of a libgomp plugin.  */ | 
|  |  | 
|  | #include <stdint.h> | 
|  | #include <stdio.h> | 
|  | #include <stdlib.h> | 
|  | #include "compiler_if_target.h" | 
|  |  | 
|  |  | 
|  | #ifdef DEBUG | 
|  | #define TRACE(...)					      \ | 
|  | {							      \ | 
|  | fprintf (stderr, "TARGET:\t%s:%s ", __FILE__, __FUNCTION__);  \ | 
|  | fprintf (stderr, __VA_ARGS__);				      \ | 
|  | fprintf (stderr, "\n");					      \ | 
|  | } | 
|  | #else | 
|  | #define TRACE { } | 
|  | #endif | 
|  |  | 
|  |  | 
|  | static VarDesc vd_host2tgt = { | 
|  | { 1, 1 },		      /* dst, src			      */ | 
|  | { 1, 0 },		      /* in, out			      */ | 
|  | 1,			      /* alloc_if			      */ | 
|  | 1,			      /* free_if			      */ | 
|  | 4,			      /* align				      */ | 
|  | 0,			      /* mic_offset			      */ | 
|  | { 0, 0, 0, 0, 0, 0, 0, 0 }, /* is_static, is_static_dstn, has_length, | 
|  | is_stack_buf, sink_addr, alloc_disp, | 
|  | is_noncont_src, is_noncont_dst	      */ | 
|  | 0,			      /* offset				      */ | 
|  | 0,			      /* size				      */ | 
|  | 1,			      /* count				      */ | 
|  | 0,			      /* alloc				      */ | 
|  | 0,			      /* into				      */ | 
|  | 0			      /* ptr				      */ | 
|  | }; | 
|  |  | 
|  | static VarDesc vd_tgt2host = { | 
|  | { 1, 1 },		      /* dst, src			      */ | 
|  | { 0, 1 },		      /* in, out			      */ | 
|  | 1,			      /* alloc_if			      */ | 
|  | 1,			      /* free_if			      */ | 
|  | 4,			      /* align				      */ | 
|  | 0,			      /* mic_offset			      */ | 
|  | { 0, 0, 0, 0, 0, 0, 0, 0 }, /* is_static, is_static_dstn, has_length, | 
|  | is_stack_buf, sink_addr, alloc_disp, | 
|  | is_noncont_src, is_noncont_dst	      */ | 
|  | 0,			      /* offset				      */ | 
|  | 0,			      /* size				      */ | 
|  | 1,			      /* count				      */ | 
|  | 0,			      /* alloc				      */ | 
|  | 0,			      /* into				      */ | 
|  | 0			      /* ptr				      */ | 
|  | }; | 
|  |  | 
|  | /* Pointer to the descriptor of the last loaded shared library.  */ | 
|  | static void *last_loaded_library = NULL; | 
|  |  | 
|  | /* Pointer and size of the variable, used in __offload_target_host2tgt_p[12] | 
|  | and __offload_target_tgt2host_p[12].  */ | 
|  | static void *last_var_ptr = NULL; | 
|  | static int last_var_size = 0; | 
|  |  | 
|  |  | 
|  | /* Override the corresponding functions from libgomp.  */ | 
|  | extern "C" int | 
|  | omp_is_initial_device (void) __GOMP_NOTHROW | 
|  | { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | extern "C" int32_t | 
|  | omp_is_initial_device_ (void) | 
|  | { | 
|  | return omp_is_initial_device (); | 
|  | } | 
|  |  | 
|  |  | 
|  | /* Dummy function needed for the initialization of target process during the | 
|  | first call to __offload_offload1.  */ | 
|  | static void | 
|  | __offload_target_init_proc (OFFLOAD ofldt) | 
|  | { | 
|  | TRACE (""); | 
|  | } | 
|  |  | 
|  | /* Collect addresses of the offload functions and of the global variables from | 
|  | the library descriptor and send them to host. | 
|  | Part 1: Send num_funcs and num_vars to host.  */ | 
|  | static void | 
|  | __offload_target_table_p1 (OFFLOAD ofldt) | 
|  | { | 
|  | void ***lib_descr = (void ***) last_loaded_library; | 
|  |  | 
|  | if (lib_descr == NULL) | 
|  | { | 
|  | TRACE (""); | 
|  | fprintf (stderr, "Error! No shared libraries loaded on target.\n"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | void **func_table_begin = lib_descr[0]; | 
|  | void **func_table_end   = lib_descr[1]; | 
|  | void **var_table_begin  = lib_descr[2]; | 
|  | void **var_table_end    = lib_descr[3]; | 
|  |  | 
|  | /* The func table contains only addresses, the var table contains addresses | 
|  | and corresponding sizes.  */ | 
|  | int num_funcs = func_table_end - func_table_begin; | 
|  | int num_vars = (var_table_end - var_table_begin) / 2; | 
|  | TRACE ("(num_funcs = %d, num_vars = %d)", num_funcs, num_vars); | 
|  |  | 
|  | VarDesc vd[2] = { vd_tgt2host, vd_tgt2host }; | 
|  | vd[0].ptr = &num_funcs; | 
|  | vd[0].size = sizeof (num_funcs); | 
|  | vd[1].ptr = &num_vars; | 
|  | vd[1].size = sizeof (num_vars); | 
|  |  | 
|  | __offload_target_enter (ofldt, 2, vd, NULL); | 
|  | __offload_target_leave (ofldt); | 
|  | } | 
|  |  | 
|  | /* Part 2: Send the table with addresses to host.  */ | 
|  | static void | 
|  | __offload_target_table_p2 (OFFLOAD ofldt) | 
|  | { | 
|  | void ***lib_descr = (void ***) last_loaded_library; | 
|  | void **func_table_begin = lib_descr[0]; | 
|  | void **func_table_end   = lib_descr[1]; | 
|  | void **var_table_begin  = lib_descr[2]; | 
|  | void **var_table_end    = lib_descr[3]; | 
|  |  | 
|  | int num_funcs = func_table_end - func_table_begin; | 
|  | int num_vars = (var_table_end - var_table_begin) / 2; | 
|  | int table_size = (num_funcs + 2 * num_vars) * sizeof (void *); | 
|  | void **table = (void **) malloc (table_size); | 
|  | TRACE ("(table_size = %d)", table_size); | 
|  |  | 
|  | VarDesc vd = vd_tgt2host; | 
|  | vd.ptr = table; | 
|  | vd.size = table_size; | 
|  |  | 
|  | __offload_target_enter (ofldt, 1, &vd, NULL); | 
|  |  | 
|  | void **p; | 
|  | int i = 0; | 
|  | for (p = func_table_begin; p < func_table_end; p++, i++) | 
|  | table[i] = *p; | 
|  |  | 
|  | for (p = var_table_begin; p < var_table_end; p++, i++) | 
|  | table[i] = *p; | 
|  |  | 
|  | __offload_target_leave (ofldt); | 
|  | free (table); | 
|  | } | 
|  |  | 
|  | /* Allocate size bytes and send a pointer to the allocated memory to host.  */ | 
|  | static void | 
|  | __offload_target_alloc (OFFLOAD ofldt) | 
|  | { | 
|  | size_t size = 0; | 
|  | void *ptr = NULL; | 
|  |  | 
|  | VarDesc vd[2] = { vd_host2tgt, vd_tgt2host }; | 
|  | vd[0].ptr = &size; | 
|  | vd[0].size = sizeof (size); | 
|  | vd[1].ptr = &ptr; | 
|  | vd[1].size = sizeof (void *); | 
|  |  | 
|  | __offload_target_enter (ofldt, 2, vd, NULL); | 
|  | ptr = malloc (size); | 
|  | TRACE ("(size = %d): ptr = %p", size, ptr); | 
|  | __offload_target_leave (ofldt); | 
|  | } | 
|  |  | 
|  | /* Free the memory space pointed to by ptr.  */ | 
|  | static void | 
|  | __offload_target_free (OFFLOAD ofldt) | 
|  | { | 
|  | void *ptr = 0; | 
|  |  | 
|  | VarDesc vd = vd_host2tgt; | 
|  | vd.ptr = &ptr; | 
|  | vd.size = sizeof (void *); | 
|  |  | 
|  | __offload_target_enter (ofldt, 1, &vd, NULL); | 
|  | TRACE ("(ptr = %p)", ptr); | 
|  | free (ptr); | 
|  | __offload_target_leave (ofldt); | 
|  | } | 
|  |  | 
|  | /* Receive var_size bytes from host and store to var_ptr. | 
|  | Part 1: Receive var_ptr and var_size from host.  */ | 
|  | static void | 
|  | __offload_target_host2tgt_p1 (OFFLOAD ofldt) | 
|  | { | 
|  | void *var_ptr = NULL; | 
|  | size_t var_size = 0; | 
|  |  | 
|  | VarDesc vd[2] = { vd_host2tgt, vd_host2tgt }; | 
|  | vd[0].ptr = &var_ptr; | 
|  | vd[0].size = sizeof (void *); | 
|  | vd[1].ptr = &var_size; | 
|  | vd[1].size = sizeof (var_size); | 
|  |  | 
|  | __offload_target_enter (ofldt, 2, vd, NULL); | 
|  | TRACE ("(var_ptr = %p, var_size = %d)", var_ptr, var_size); | 
|  | last_var_ptr = var_ptr; | 
|  | last_var_size = var_size; | 
|  | __offload_target_leave (ofldt); | 
|  | } | 
|  |  | 
|  | /* Part 2: Receive the data from host.  */ | 
|  | static void | 
|  | __offload_target_host2tgt_p2 (OFFLOAD ofldt) | 
|  | { | 
|  | TRACE ("(last_var_ptr = %p, last_var_size = %d)", | 
|  | last_var_ptr, last_var_size); | 
|  |  | 
|  | VarDesc vd = vd_host2tgt; | 
|  | vd.ptr = last_var_ptr; | 
|  | vd.size = last_var_size; | 
|  |  | 
|  | __offload_target_enter (ofldt, 1, &vd, NULL); | 
|  | __offload_target_leave (ofldt); | 
|  | } | 
|  |  | 
|  | /* Send var_size bytes from var_ptr to host. | 
|  | Part 1: Receive var_ptr and var_size from host.  */ | 
|  | static void | 
|  | __offload_target_tgt2host_p1 (OFFLOAD ofldt) | 
|  | { | 
|  | void *var_ptr = NULL; | 
|  | size_t var_size = 0; | 
|  |  | 
|  | VarDesc vd[2] = { vd_host2tgt, vd_host2tgt }; | 
|  | vd[0].ptr = &var_ptr; | 
|  | vd[0].size = sizeof (void *); | 
|  | vd[1].ptr = &var_size; | 
|  | vd[1].size = sizeof (var_size); | 
|  |  | 
|  | __offload_target_enter (ofldt, 2, vd, NULL); | 
|  | TRACE ("(var_ptr = %p, var_size = %d)", var_ptr, var_size); | 
|  | last_var_ptr = var_ptr; | 
|  | last_var_size = var_size; | 
|  | __offload_target_leave (ofldt); | 
|  | } | 
|  |  | 
|  | /* Part 2: Send the data to host.  */ | 
|  | static void | 
|  | __offload_target_tgt2host_p2 (OFFLOAD ofldt) | 
|  | { | 
|  | TRACE ("(last_var_ptr = %p, last_var_size = %d)", | 
|  | last_var_ptr, last_var_size); | 
|  |  | 
|  | VarDesc vd = vd_tgt2host; | 
|  | vd.ptr = last_var_ptr; | 
|  | vd.size = last_var_size; | 
|  |  | 
|  | __offload_target_enter (ofldt, 1, &vd, NULL); | 
|  | __offload_target_leave (ofldt); | 
|  | } | 
|  |  | 
|  | /* Copy SIZE bytes from SRC_PTR to DST_PTR.  */ | 
|  | static void | 
|  | __offload_target_tgt2tgt (OFFLOAD ofldt) | 
|  | { | 
|  | void *src_ptr = NULL; | 
|  | void *dst_ptr = NULL; | 
|  | size_t size = 0; | 
|  |  | 
|  | VarDesc vd[3] = { vd_host2tgt, vd_host2tgt, vd_host2tgt }; | 
|  | vd[0].ptr = &dst_ptr; | 
|  | vd[0].size = sizeof (void *); | 
|  | vd[1].ptr = &src_ptr; | 
|  | vd[1].size = sizeof (void *); | 
|  | vd[2].ptr = &size; | 
|  | vd[2].size = sizeof (size); | 
|  |  | 
|  | __offload_target_enter (ofldt, 3, vd, NULL); | 
|  | TRACE ("(dst_ptr = %p, src_ptr = %p, size = %d)", dst_ptr, src_ptr, size); | 
|  | memcpy (dst_ptr, src_ptr, size); | 
|  | __offload_target_leave (ofldt); | 
|  | } | 
|  |  | 
|  | /* Call offload function by the address fn_ptr and pass vars_ptr to it.  */ | 
|  | static void | 
|  | __offload_target_run (OFFLOAD ofldt) | 
|  | { | 
|  | void *fn_ptr; | 
|  | void *vars_ptr; | 
|  |  | 
|  | VarDesc vd[2] = { vd_host2tgt, vd_host2tgt }; | 
|  | vd[0].ptr = &fn_ptr; | 
|  | vd[0].size = sizeof (void *); | 
|  | vd[1].ptr = &vars_ptr; | 
|  | vd[1].size = sizeof (void *); | 
|  |  | 
|  | __offload_target_enter (ofldt, 2, vd, NULL); | 
|  | TRACE ("(fn_ptr = %p, vars_ptr = %p)", fn_ptr, vars_ptr); | 
|  | void (*fn)(void *) = (void (*)(void *)) fn_ptr; | 
|  | fn (vars_ptr); | 
|  | __offload_target_leave (ofldt); | 
|  | } | 
|  |  | 
|  |  | 
|  | /* This should be called from every library with offloading.  */ | 
|  | extern "C" void | 
|  | target_register_lib (const void *target_table) | 
|  | { | 
|  | TRACE ("(target_table = %p { %p, %p, %p, %p })", target_table, | 
|  | ((void **) target_table)[0], ((void **) target_table)[1], | 
|  | ((void **) target_table)[2], ((void **) target_table)[3]); | 
|  |  | 
|  | last_loaded_library = (void *) target_table; | 
|  | } | 
|  |  | 
|  | /* Use __offload_target_main from liboffload.  */ | 
|  | int | 
|  | main (int argc, char **argv) | 
|  | { | 
|  | __offload_target_main (); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* Register offload_target_main's functions in the liboffload.  */ | 
|  |  | 
|  | struct Entry { | 
|  | const char *name; | 
|  | void *func; | 
|  | }; | 
|  |  | 
|  | #define REGISTER(f)				      \ | 
|  | extern "C" const Entry __offload_target_##f##_$entry  \ | 
|  | __attribute__ ((section(".OffloadEntryTable."))) = {  \ | 
|  | "__offload_target_"#f,			      \ | 
|  | (void *) __offload_target_##f			      \ | 
|  | } | 
|  | REGISTER (init_proc); | 
|  | REGISTER (table_p1); | 
|  | REGISTER (table_p2); | 
|  | REGISTER (alloc); | 
|  | REGISTER (free); | 
|  | REGISTER (host2tgt_p1); | 
|  | REGISTER (host2tgt_p2); | 
|  | REGISTER (tgt2host_p1); | 
|  | REGISTER (tgt2host_p2); | 
|  | REGISTER (tgt2tgt); | 
|  | REGISTER (run); | 
|  | #undef REGISTER |