/* Copyright (c) 2014-2016 Intel Corporation. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /*! \file \brief The parts of the runtime library common to host and target */ #ifndef OFFLOAD_COMMON_H_INCLUDED #define OFFLOAD_COMMON_H_INCLUDED #include #include #include #include #include "offload.h" #include "offload_table.h" #include "offload_trace.h" #include "offload_timer.h" #include "offload_util.h" #include "cean_util.h" #include "dv_util.h" #include "liboffload_error_codes.h" #include // Use secure getenv if it's supported #ifdef HAVE_SECURE_GETENV #define getenv(x) secure_getenv(x) #elif HAVE___SECURE_GETENV #define getenv(x) __secure_getenv(x) #endif // Offload Library versioning DLL_LOCAL extern int offload_version; DLL_LOCAL extern int offload_version_count; // The debug routines // Host console and file logging DLL_LOCAL extern int console_enabled; DLL_LOCAL extern int offload_report_level; DLL_LOCAL extern const char *prefix; DLL_LOCAL extern int offload_number; #if !HOST_LIBRARY DLL_LOCAL extern int mic_index; #define OFFLOAD_DO_TRACE (offload_report_level == 3) #else #define OFFLOAD_DO_TRACE (offload_report_enabled && (offload_report_level == 3)) #endif #if HOST_LIBRARY DLL_LOCAL void Offload_Report_Prolog(OffloadHostTimerData* timer_data); DLL_LOCAL void Offload_Report_Epilog(OffloadHostTimerData* timer_data); DLL_LOCAL void offload_report_free_data(OffloadHostTimerData * timer_data); DLL_LOCAL void Offload_Timer_Print(void); #ifndef TARGET_WINNT #define OFFLOAD_DEBUG_INCR_OFLD_NUM() \ __sync_add_and_fetch(&offload_number, 1) #else #define OFFLOAD_DEBUG_INCR_OFLD_NUM() \ _InterlockedIncrement(reinterpret_cast(&offload_number)) #endif #define OFFLOAD_DEBUG_PRINT_TAG_PREFIX() \ printf("%s: ", prefix); #define OFFLOAD_DEBUG_PRINT_PREFIX() \ printf("%s: ", prefix); #else #define OFFLOAD_DEBUG_PRINT_PREFIX() \ printf("%s%d: ", prefix, mic_index); #endif // HOST_LIBRARY #define OFFLOAD_TRACE(trace_level, ...) \ if (console_enabled >= trace_level) { \ OFFLOAD_DEBUG_PRINT_PREFIX(); \ printf(__VA_ARGS__); \ fflush(NULL); \ } #if OFFLOAD_DEBUG > 0 #define OFFLOAD_DEBUG_TRACE(level, ...) \ OFFLOAD_TRACE(level, __VA_ARGS__) #define OFFLOAD_REPORT(level, offload_number, stage, ...) \ if (OFFLOAD_DO_TRACE) { \ offload_stage_print(stage, offload_number, __VA_ARGS__); \ fflush(NULL); \ } #define OFFLOAD_DEBUG_TRACE_1(level, offload_number, stage, ...) \ if (OFFLOAD_DO_TRACE) { \ offload_stage_print(stage, offload_number, __VA_ARGS__); \ fflush(NULL); \ } \ if (!OFFLOAD_DO_TRACE) { \ OFFLOAD_TRACE(level, __VA_ARGS__) \ } #define OFFLOAD_DEBUG_DUMP_BYTES(level, a, b) \ __dump_bytes(level, a, b) DLL_LOCAL extern void __dump_bytes( int level, const void *data, int len ); #else #define OFFLOAD_DEBUG_LOG(level, ...) #define OFFLOAD_DEBUG_DUMP_BYTES(level, a, b) #endif // Runtime interface #define OFFLOAD_PREFIX(a) __offload_##a #define OFFLOAD_MALLOC OFFLOAD_PREFIX(malloc) #define OFFLOAD_FREE(a) _mm_free(a) // Forward functions extern void *OFFLOAD_MALLOC(size_t size, size_t align); // The Marshaller // Flags describing an offload //! Flags describing an offload union OffloadFlags{ uint32_t flags; struct { uint32_t fortran_traceback : 1; //!< Fortran traceback requested uint32_t omp_async : 1; //!< OpenMP asynchronous offload } bits; }; //! \enum Indicator for the type of entry on an offload item list. enum OffloadItemType { c_data = 1, //!< Plain data c_data_ptr, //!< Pointer data c_func_ptr, //!< Function pointer c_void_ptr, //!< void* c_string_ptr, //!< C string c_dv, //!< Dope vector variable c_dv_data, //!< Dope-vector data c_dv_data_slice, //!< Dope-vector data's slice c_dv_ptr, //!< Dope-vector variable pointer c_dv_ptr_data, //!< Dope-vector pointer data c_dv_ptr_data_slice,//!< Dope-vector pointer data's slice c_cean_var, //!< CEAN variable c_cean_var_ptr, //!< Pointer to CEAN variable c_data_ptr_array, //!< Pointer to data pointer array c_extended_type, //!< Is used to extend OffloadItemType //!< Actual OffloadItemType is in the //!< structure VarDescExtendedType c_func_ptr_array, //!< Pointer to function pointer array c_void_ptr_array, //!< Pointer to void* pointer array c_string_ptr_array, //!< Pointer to char* pointer array c_data_ptr_ptr, //!< Pointer to pointer to data (struct member) c_func_ptr_ptr, //!< Pointer to pointer to function (struct member) c_void_ptr_ptr, //!< Pointer to pointer to void* (struct member) c_string_ptr_ptr, //!< Pointer to pointer to string (struct member) c_cean_var_ptr_ptr //!< Pointer to pointer to cean var (struct member) }; #define TYPE_IS_PTR_TO_PTR(t) ((t) == c_string_ptr_ptr || \ (t) == c_data_ptr_ptr || \ (t) == c_func_ptr_ptr || \ (t) == c_void_ptr_ptr || \ (t) == c_cean_var_ptr_ptr) #define VAR_TYPE_IS_PTR(t) ((t) == c_string_ptr || \ (t) == c_data_ptr || \ (t) == c_cean_var_ptr || \ (t) == c_dv_ptr || \ TYPE_IS_PTR_TO_PTR(t)) #define VAR_TYPE_IS_SCALAR(t) ((t) == c_data || \ (t) == c_void_ptr || \ (t) == c_cean_var || \ (t) == c_dv) #define VAR_TYPE_IS_DV_DATA(t) ((t) == c_dv_data || \ (t) == c_dv_ptr_data) #define VAR_TYPE_IS_DV_DATA_SLICE(t) ((t) == c_dv_data_slice || \ (t) == c_dv_ptr_data_slice) //! \enum Specify direction to copy offloaded variable. enum OffloadParameterType { c_parameter_unknown = -1, //!< Unknown clause c_parameter_nocopy, //!< Variable listed in "nocopy" clause c_parameter_in, //!< Variable listed in "in" clause c_parameter_out, //!< Variable listed in "out" clause c_parameter_inout //!< Variable listed in "inout" clause }; //! Flags describing an offloaded variable union varDescFlags { struct { //! source variable has persistent storage uint32_t is_static : 1; //! destination variable has persistent storage uint32_t is_static_dstn : 1; //! has length for c_dv && c_dv_ptr uint32_t has_length : 1; //! persisted local scalar is in stack buffer uint32_t is_stack_buf : 1; //! "targetptr" modifier used uint32_t targetptr : 1; //! "preallocated" modifier used uint32_t preallocated : 1; //! pointer to a pointer array uint32_t is_pointer : 1; //! buffer address is sent in data uint32_t sink_addr : 1; //! alloc displacement is sent in data uint32_t alloc_disp : 1; //! source data is noncontiguous uint32_t is_noncont_src : 1; //! destination data is noncontiguous uint32_t is_noncont_dst : 1; //! "OpenMP always" modifier used uint32_t always_copy : 1; //! "OpenMP delete" modifier used uint32_t always_delete : 1; //! structured data is noncontiguous uint32_t is_non_cont_struct : 1; //! CPU memory pinning/unpinning operation uint32_t pin : 1; //! Pointer to device memory uint32_t is_device_ptr : 1; //! Hostpointer with associated device pointer uint32_t use_device_ptr : 1; }; uint32_t bits; }; //! An Offload Variable descriptor struct VarDesc { //! OffloadItemTypes of source and destination union { struct { uint8_t dst : 4; //!< OffloadItemType of destination uint8_t src : 4; //!< OffloadItemType of source }; uint8_t bits; } type; //! OffloadParameterType that describes direction of data transfer union { struct { uint8_t in : 1; //!< Set if IN or INOUT uint8_t out : 1; //!< Set if OUT or INOUT }; uint8_t bits; } direction; uint8_t alloc_if; //!< alloc_if modifier value uint8_t free_if; //!< free_if modifier value uint32_t align; //!< MIC alignment requested for pointer data //! Not used by compiler; set to 0 /*! Used by runtime as offset to data from start of MIC buffer */ uint32_t mic_offset; //! Flags describing this variable varDescFlags flags; //! Not used by compiler; set to 0 /*! Used by runtime as offset to base from data stored in a buffer */ int64_t offset; //! Element byte-size of data to be transferred /*! For dope-vector, the size of the dope-vector */ int64_t size; union { //! Set to 0 for array expressions and dope-vectors /*! Set to 1 for scalars */ /*! Set to value of length modifier for pointers */ int64_t count; //! Displacement not used by compiler int64_t disp; }; //! This field not used by OpenMP 4.0 /*! The alloc section expression in #pragma offload */ union { void *alloc; int64_t ptr_arr_offset; }; //! This field not used by OpenMP 4.0 /*! The into section expression in #pragma offload */ /*! For c_data_ptr_array this is the into ptr array */ void *into; //! For an ordinary variable, address of the variable /*! For c_cean_var (C/C++ array expression), pointer to arr_desc, which is an array descriptor. */ /*! For c_data_ptr_array (array of data pointers), pointer to ptr_array_descriptor, which is a descriptor for pointer array transfers. */ void *ptr; }; //! Auxiliary struct used when -g is enabled that holds variable names struct VarDesc2 { const char *sname; //!< Source name const char *dname; //!< Destination name (when "into" is used) }; /*! When the OffloadItemType is c_data_ptr_array the ptr field of the main descriptor points to this struct. */ /*! The type in VarDesc1 merely says c_cean_data_ptr, but the pointer type can be c_data_ptr, c_func_ptr, c_void_ptr, or c_string_ptr. Therefore the actual pointer type is in the flags field of VarDesc3. */ /*! If flag_align_is_array/flag_alloc_if_is_array/flag_free_if_is_array is 0 then alignment/alloc_if/free_if are specified in VarDesc1. */ /*! If flag_align_is_array/flag_alloc_if_is_array/flag_free_if_is_array is 1 then align_array/alloc_if_array/free_if_array specify the set of alignment/alloc_if/free_if values. */ /*! For the other fields, if neither the scalar nor the array flag is set, then that modifier was not specified. If the bits are set they specify which modifier was set and whether it was a scalar or an array expression. */ struct VarDesc3 { void *ptr_array; //!< Pointer to arr_desc of array of pointers void *align_array; //!< Scalar value or pointer to arr_desc void *alloc_if_array; //!< Scalar value or pointer to arr_desc void *free_if_array; //!< Scalar value or pointer to arr_desc void *extent_start; //!< Scalar value or pointer to arr_desc void *extent_elements; //!< Scalar value or pointer to arr_desc void *into_start; //!< Scalar value or pointer to arr_desc void *into_elements; //!< Scalar value or pointer to arr_desc void *alloc_start; //!< Scalar value or pointer to arr_desc void *alloc_elements; //!< Scalar value or pointer to arr_desc /*! Flags that describe the pointer type and whether each field is a scalar value or an array expression. */ /*! First 6 bits are pointer array element type: c_data_ptr, c_func_ptr, c_void_ptr, c_string_ptr */ /*! Then single bits specify: */ /*! align_array is an array */ /*! alloc_if_array is an array */ /*! free_if_array is an array */ /*! extent_start is a scalar expression */ /*! extent_start is an array expression */ /*! extent_elements is a scalar expression */ /*! extent_elements is an array expression */ /*! into_start is a scalar expression */ /*! into_start is an array expression */ /*! into_elements is a scalar expression */ /*! into_elements is an array expression */ /*! alloc_start is a scalar expression */ /*! alloc_start is an array expression */ /*! alloc_elements is a scalar expression */ /*! alloc_elements is an array expression */ uint32_t array_fields; }; const int flag_align_is_array = 6; const int flag_alloc_if_is_array = 7; const int flag_free_if_is_array = 8; const int flag_extent_start_is_scalar = 9; const int flag_extent_start_is_array = 10; const int flag_extent_elements_is_scalar = 11; const int flag_extent_elements_is_array = 12; const int flag_into_start_is_scalar = 13; const int flag_into_start_is_array = 14; const int flag_into_elements_is_scalar = 15; const int flag_into_elements_is_array = 16; const int flag_alloc_start_is_scalar = 17; const int flag_alloc_start_is_array = 18; const int flag_alloc_elements_is_scalar = 19; const int flag_alloc_elements_is_array = 20; //! Extended Variable Descriptor. Since VarDesc uses 16 bits for //! OffloadItemType, we have exceeded that limit, So any Type //! greater than 15 will have Type set in VarDesc as c_extended_type //! and this structure will be used to represent those Types. typedef struct VarDescExtendedType { // Represents overflow of OffloadItemType uint32_t extended_type; //! For extended_type //! address of the variable //! Future Types can point to other descriptors void *ptr; } VarDescExtendedType; // The Marshaller class Marshaller { private: // Start address of buffer char *buffer_start; // Current pointer within buffer char *buffer_ptr; // Physical size of data sent (including flags) long long buffer_size; // User data sent/received long long tfr_size; public: // Constructor Marshaller() : buffer_start(0), buffer_ptr(0), buffer_size(0), tfr_size(0) { } // Return count of user data sent/received long long get_tfr_size() const { return tfr_size; } // Return pointer to buffer char *get_buffer_start() const { return buffer_start; } // Return current size of data in buffer long long get_buffer_size() const { return buffer_size; } // Set buffer pointer void init_buffer( char *d, long long s ) { buffer_start = buffer_ptr = d; buffer_size = s; } // Send data void send_data( const void *data, int64_t length ); // Receive data void receive_data( void *data, int64_t length ); // Send function pointer void send_func_ptr( const void* data ); // Receive function pointer void receive_func_ptr( const void** data ); }; // End of the Marshaller // The offloaded function descriptor. // Sent from host to target to specify which function to run. // Also, sets console and file tracing levels. struct FunctionDescriptor { // Input data size. long long in_datalen; // Output data size. long long out_datalen; // Whether trace is requested on console. // A value of 1 produces only function name and data sent/received. // Values > 1 produce copious trace information. uint8_t console_enabled; // Flag controlling timing on the target side. // Values > 0 enable timing on sink. uint8_t timer_enabled; int offload_report_level; int offload_number; // number of variable descriptors int vars_num; // inout data offset if data is passed as misc/return data // otherwise it should be zero. int data_offset; // The name of the offloaded function char data[]; }; // typedef OFFLOAD. // Pointer to OffloadDescriptor. typedef struct OffloadDescriptor *OFFLOAD; // Use for setting affinity of a stream enum affinity_type { affinity_compact, affinity_scatter }; struct affinity_spec { uint64_t sink_mask[16]; int affinity_type; int num_cores; int num_threads; }; #endif // OFFLOAD_COMMON_H_INCLUDED