mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			368 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			368 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
| /* Plugin for offload execution on Intel MIC devices.
 | |
| 
 | |
|    Copyright (C) 2014 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 vd1[2] = { vd_tgt2host, vd_tgt2host };
 | |
|   vd1[0].ptr = &num_funcs;
 | |
|   vd1[0].size = sizeof (num_funcs);
 | |
|   vd1[1].ptr = &num_vars;
 | |
|   vd1[1].size = sizeof (num_vars);
 | |
|   VarDesc2 vd2[2] = { { "num_funcs", 0 }, { "num_vars", 0 } };
 | |
| 
 | |
|   __offload_target_enter (ofldt, 2, vd1, vd2);
 | |
|   __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 vd1;
 | |
|   vd1 = vd_tgt2host;
 | |
|   vd1.ptr = table;
 | |
|   vd1.size = table_size;
 | |
|   VarDesc2 vd2 = { "table", 0 };
 | |
| 
 | |
|   __offload_target_enter (ofldt, 1, &vd1, &vd2);
 | |
| 
 | |
|   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 vd1[2] = { vd_host2tgt, vd_tgt2host };
 | |
|   vd1[0].ptr = &size;
 | |
|   vd1[0].size = sizeof (size);
 | |
|   vd1[1].ptr = &ptr;
 | |
|   vd1[1].size = sizeof (void *);
 | |
|   VarDesc2 vd2[2] = { { "size", 0 }, { "ptr", 0 } };
 | |
| 
 | |
|   __offload_target_enter (ofldt, 2, vd1, vd2);
 | |
|   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 vd1 = vd_host2tgt;
 | |
|   vd1.ptr = &ptr;
 | |
|   vd1.size = sizeof (void *);
 | |
|   VarDesc2 vd2 = { "ptr", 0 };
 | |
| 
 | |
|   __offload_target_enter (ofldt, 1, &vd1, &vd2);
 | |
|   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 vd1[2] = { vd_host2tgt, vd_host2tgt };
 | |
|   vd1[0].ptr = &var_ptr;
 | |
|   vd1[0].size = sizeof (void *);
 | |
|   vd1[1].ptr = &var_size;
 | |
|   vd1[1].size = sizeof (var_size);
 | |
|   VarDesc2 vd2[2] = { { "var_ptr", 0 }, { "var_size", 0 } };
 | |
| 
 | |
|   __offload_target_enter (ofldt, 2, vd1, vd2);
 | |
|   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 vd1 = vd_host2tgt;
 | |
|   vd1.ptr = last_var_ptr;
 | |
|   vd1.size = last_var_size;
 | |
|   VarDesc2 vd2 = { "var", 0 };
 | |
| 
 | |
|   __offload_target_enter (ofldt, 1, &vd1, &vd2);
 | |
|   __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 vd1[2] = { vd_host2tgt, vd_host2tgt };
 | |
|   vd1[0].ptr = &var_ptr;
 | |
|   vd1[0].size = sizeof (void *);
 | |
|   vd1[1].ptr = &var_size;
 | |
|   vd1[1].size = sizeof (var_size);
 | |
|   VarDesc2 vd2[2] = { { "var_ptr", 0 }, { "var_size", 0 } };
 | |
| 
 | |
|   __offload_target_enter (ofldt, 2, vd1, vd2);
 | |
|   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 vd1 = vd_tgt2host;
 | |
|   vd1.ptr = last_var_ptr;
 | |
|   vd1.size = last_var_size;
 | |
|   VarDesc2 vd2 = { "var", 0 };
 | |
| 
 | |
|   __offload_target_enter (ofldt, 1, &vd1, &vd2);
 | |
|   __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 vd1[2] = { vd_host2tgt, vd_host2tgt };
 | |
|   vd1[0].ptr = &fn_ptr;
 | |
|   vd1[0].size = sizeof (void *);
 | |
|   vd1[1].ptr = &vars_ptr;
 | |
|   vd1[1].size = sizeof (void *);
 | |
|   VarDesc2 vd2[2] = { { "fn_ptr", 0 }, { "vars_ptr", 0 } };
 | |
| 
 | |
|   __offload_target_enter (ofldt, 2, vd1, vd2);
 | |
|   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 (run);
 | |
| #undef REGISTER
 |