| /* 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 |