1 /*
2     Copyright (c) 2014-2016 Intel Corporation.  All Rights Reserved.
3 
4     Redistribution and use in source and binary forms, with or without
5     modification, are permitted provided that the following conditions
6     are met:
7 
8       * Redistributions of source code must retain the above copyright
9         notice, this list of conditions and the following disclaimer.
10       * Redistributions in binary form must reproduce the above copyright
11         notice, this list of conditions and the following disclaimer in the
12         documentation and/or other materials provided with the distribution.
13       * Neither the name of Intel Corporation nor the names of its
14         contributors may be used to endorse or promote products derived
15         from this software without specific prior written permission.
16 
17     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18     "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19     LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20     A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21     HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23     LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24     DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25     THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26     (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27     OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29 
30 
31 #include "offload_table.h"
32 #include "offload_common.h"
33 
34 // Offload Library versioning
35 // We initialize version to OFFLOAD_VERSION_16
36 // 15.0 application downgrades this to 1500 for MYO to use the older version.
37 // 15.0 pragma works without needing version-specific code.
38 // 16.0-U2 added a call from ofldbegin.cpp to set the version explicitly.
39 // Pre-16.0-U2 application will find pre-initialized version number as 1600.
40 // Post 16.0-U2 application will set its own version explicitly.
41 int offload_version = OFFLOAD_VERSION_16;
42 int offload_version_count = 0;
43 
44 #if !HOST_LIBRARY
45 // Predefined offload entries
46 extern void omp_set_num_threads_lrb(void*);
47 extern void omp_get_max_threads_lrb(void*);
48 extern void omp_get_num_procs_lrb(void*);
49 extern void omp_set_dynamic_lrb(void*);
50 extern void omp_get_dynamic_lrb(void*);
51 extern void omp_set_nested_lrb(void*);
52 extern void omp_get_nested_lrb(void*);
53 extern void omp_set_schedule_lrb(void*);
54 extern void omp_get_schedule_lrb(void*);
55 
56 extern void omp_init_lock_lrb(void*);
57 extern void omp_destroy_lock_lrb(void*);
58 extern void omp_set_lock_lrb(void*);
59 extern void omp_unset_lock_lrb(void*);
60 extern void omp_test_lock_lrb(void*);
61 
62 extern void omp_init_nest_lock_lrb(void*);
63 extern void omp_destroy_nest_lock_lrb(void*);
64 extern void omp_set_nest_lock_lrb(void*);
65 extern void omp_unset_nest_lock_lrb(void*);
66 extern void omp_test_nest_lock_lrb(void*);
67 
68 // OpenMP 4.5 APIs
69 extern void omp_target_alloc_target(void*);
70 extern void omp_target_free_target(void*);
71 extern void omp_target_memcpy_target(void*);
72 extern void omp_target_memcpy_rect_target(void*);
73 
74 // Predefined entries on the target side
75 static FuncTable::Entry predefined_entries[] = {
76     "omp_set_num_threads_target",
77     (void*) &omp_set_num_threads_lrb,
78     "omp_get_max_threads_target",
79     (void*) &omp_get_max_threads_lrb,
80     "omp_get_num_procs_target",
81     (void*) &omp_get_num_procs_lrb,
82     "omp_set_dynamic_target",
83     (void*) &omp_set_dynamic_lrb,
84     "omp_get_dynamic_target",
85     (void*) &omp_get_dynamic_lrb,
86     "omp_set_nested_target",
87     (void*) &omp_set_nested_lrb,
88     "omp_get_nested_target",
89     (void*) &omp_get_nested_lrb,
90     "omp_set_schedule_target",
91     (void*) &omp_set_schedule_lrb,
92     "omp_get_schedule_target",
93     (void*) &omp_get_schedule_lrb,
94 
95     "omp_init_lock_target",
96     (void*) &omp_init_lock_lrb,
97     "omp_destroy_lock_target",
98     (void*) &omp_destroy_lock_lrb,
99     "omp_set_lock_target",
100     (void*) &omp_set_lock_lrb,
101     "omp_unset_lock_target",
102     (void*) &omp_unset_lock_lrb,
103     "omp_test_lock_target",
104     (void*) &omp_test_lock_lrb,
105 
106     "omp_init_nest_lock_target",
107     (void*) &omp_init_nest_lock_lrb,
108     "omp_destroy_nest_lock_target",
109     (void*) &omp_destroy_nest_lock_lrb,
110     "omp_set_nest_lock_target",
111     (void*) &omp_set_nest_lock_lrb,
112     "omp_unset_nest_lock_target",
113     (void*) &omp_unset_nest_lock_lrb,
114     "omp_test_nest_lock_target",
115     (void*) &omp_test_nest_lock_lrb,
116 
117     "omp_target_alloc_target",
118     (void*) &omp_target_alloc_target,
119     "omp_target_free_target",
120     (void*) &omp_target_free_target,
121     "omp_target_memcpy_target",
122     (void*) &omp_target_memcpy_target,
123     "omp_target_memcpy_rect_target",
124     (void*) &omp_target_memcpy_rect_target,
125 
126     (const char*) -1,
127     (void*) -1
128 };
129 
130 static FuncList::Node predefined_table = {
131     { predefined_entries, -1 },
132     0, 0
133 };
134 
135 // Entry table
136 FuncList __offload_entries(&predefined_table);
137 #else
138 FuncList __offload_entries;
139 #endif // !HOST_LIBRARY
140 
141 extern "C" {
142 
143 // Set library version
__offload_set_version(int v)144 void __offload_set_version(int v)
145 {
146     offload_version_count++;
147     if (offload_version_count == 1)
148     {
149         offload_version = v;
150     }
151     else
152     {
153         // Mix of versions is not supported
154         if (v != offload_version)
155         {
156             LIBOFFLOAD_ERROR(c_mixed_versions);
157             exit(1);
158         }
159     }
160 }
161 
162 }  // extern "C"
163 // Function table. No predefined entries.
164 FuncList __offload_funcs;
165 
166 // Var table
167 VarList  __offload_vars;
168 
169 // Given the function name returns the associtated function pointer
find_addr(const char * name)170 const void* FuncList::find_addr(const char *name)
171 {
172     const void* func = 0;
173 
174     m_lock.lock();
175 
176     for (Node *n = m_head; n != 0; n = n->next) {
177         for (const Table::Entry *e = n->table.entries;
178              e->name != (const char*) -1; e++) {
179             if (e->name != 0 && strcmp(e->name, name) == 0) {
180                 func = e->func;
181                 break;
182             }
183         }
184     }
185 
186     m_lock.unlock();
187 
188     return func;
189 }
190 
191 // Given the function pointer returns the associtated function name
find_name(const void * func)192 const char* FuncList::find_name(const void *func)
193 {
194     const char* name = 0;
195 
196     m_lock.lock();
197 
198     for (Node *n = m_head; n != 0; n = n->next) {
199         for (const Table::Entry *e = n->table.entries;
200              e->name != (const char*) -1; e++) {
201             if (e->func == func) {
202                 name = e->name;
203                 break;
204             }
205         }
206     }
207 
208     m_lock.unlock();
209 
210     return name;
211 }
212 
213 // Returns max name length from all tables
max_name_length(void)214 int64_t FuncList::max_name_length(void)
215 {
216     if (m_max_name_len < 0) {
217         m_lock.lock();
218 
219         m_max_name_len = 0;
220         for (Node *n = m_head; n != 0; n = n->next) {
221             if (n->table.max_name_len < 0) {
222                 n->table.max_name_len = 0;
223 
224                 // calculate max name length in a single table
225                 for (const Table::Entry *e = n->table.entries;
226                      e->name != (const char*) -1; e++) {
227                     if (e->name != 0) {
228                         size_t len = strlen(e->name) + 1;
229                         if (n->table.max_name_len < len) {
230                             n->table.max_name_len = len;
231                         }
232                     }
233                 }
234             }
235 
236             // select max from all tables
237             if (m_max_name_len < n->table.max_name_len) {
238                 m_max_name_len = n->table.max_name_len;
239             }
240         }
241 
242         m_lock.unlock();
243     }
244     return m_max_name_len;
245 }
246 
247 // Debugging dump
dump(void)248 void FuncList::dump(void)
249 {
250     OFFLOAD_DEBUG_TRACE(2, "Function table:\n");
251 
252     m_lock.lock();
253 
254     for (Node *n = m_head; n != 0; n = n->next) {
255         for (const Table::Entry *e = n->table.entries;
256              e->name != (const char*) -1; e++) {
257             if (e->name != 0) {
258                 OFFLOAD_DEBUG_TRACE(2, "%p %s\n", e->func, e->name);
259             }
260         }
261     }
262 
263     m_lock.unlock();
264 }
265 
266 // Debugging dump
dump(void)267 void VarList::dump(void)
268 {
269     OFFLOAD_DEBUG_TRACE(2, "Var table:\n");
270 
271     m_lock.lock();
272 
273     for (Node *n = m_head; n != 0; n = n->next) {
274         for (const Table::Entry *e = n->table.entries;
275              e->name != (const char*) -1; e++) {
276             if (e->name != 0) {
277 #if HOST_LIBRARY
278                 OFFLOAD_DEBUG_TRACE(2, "%s %p %ld\n", e->name, e->addr,
279                                     e->size);
280 #else  // HOST_LIBRARY
281                 OFFLOAD_DEBUG_TRACE(2, "%s %p\n", e->name, e->addr);
282 #endif // HOST_LIBRARY
283             }
284         }
285     }
286 
287     m_lock.unlock();
288 }
289 
290 //
table_size(int64_t & nelems)291 int64_t VarList::table_size(int64_t &nelems)
292 {
293     int64_t length = 0;
294 
295     nelems = 0;
296 
297     // calculate string table size and number of elements
298     for (Node *n = m_head; n != 0; n = n->next) {
299         for (const Table::Entry *e = n->table.entries;
300              e->name != (const char*) -1; e++) {
301             if (e->name != 0) {
302                 length += strlen(e->name) + 1;
303                 nelems++;
304             }
305         }
306     }
307 
308     return nelems * sizeof(BufEntry) + length;
309 }
310 
311 // copy table to the gven buffer
table_copy(void * buf,int64_t nelems)312 void VarList::table_copy(void *buf, int64_t nelems)
313 {
314     BufEntry* elems = static_cast<BufEntry*>(buf);
315     char*     names = reinterpret_cast<char*>(elems + nelems);
316 
317     // copy entries to buffer
318     for (Node *n = m_head; n != 0; n = n->next) {
319         for (const Table::Entry *e = n->table.entries;
320              e->name != (const char*) -1; e++) {
321             if (e->name != 0) {
322                 // name field contains offset to the name from the beginning
323                 // of the buffer
324                 elems->name = names - static_cast<char*>(buf);
325                 elems->addr = reinterpret_cast<intptr_t>(e->addr);
326 
327                 // copy name to string table
328                 const char *name = e->name;
329                 while ((*names++ = *name++) != '\0');
330 
331                 elems++;
332             }
333         }
334     }
335 }
336 
337 // patch name offsets in a buffer
table_patch_names(void * buf,int64_t nelems)338 void VarList::table_patch_names(void *buf, int64_t nelems)
339 {
340     BufEntry* elems = static_cast<BufEntry*>(buf);
341     for (int i = 0; i < nelems; i++) {
342         elems[i].name += reinterpret_cast<intptr_t>(buf);
343     }
344 }
345 
346 #if HOST_LIBRARY
347 // 16.0 and earlier compilers used the following VarTable
348 struct OldVarTable {
349     const char* name;
350     void*       addr;
351     // uint64_t var_alloc_type  missing in 16.0 and earlier
352     uint64_t    size;
353 };
354 
convert_OldVarTable_to_NewVarTable(VarList::Node * vt_start)355 static void convert_OldVarTable_to_NewVarTable(VarList::Node *vt_start)
356 {
357     int table_size = 0;
358     char * new_var_table;
359     OldVarTable *old_var_table;
360 
361     OFFLOAD_DEBUG_TRACE(2,
362        "Converting old var table to new var table to support backward compatiblity\n");
363 
364     // Calculate size of memory to be malloced
365     old_var_table = (OldVarTable *) vt_start->table.entries;
366     while (old_var_table->name != (const char*) -1) {
367         table_size++;
368         old_var_table++;
369     }
370 
371     if (table_size != 0) {
372        // Add 1 to table_size for end of table signature
373        VarTable::Entry *new_var_table =
374            new VarTable::Entry[table_size+1];
375 
376        if (new_var_table  == NULL)
377           LIBOFFLOAD_ERROR(c_malloc);
378 
379       old_var_table = (OldVarTable *) vt_start->table.entries;
380 
381       // Update VarList with new table
382       vt_start->table.entries = new_var_table;
383 
384       // Fix up the new table value from old table
385       for (int i=0; i< table_size; i++) {
386          new_var_table->name = old_var_table->name;
387          new_var_table->addr = old_var_table->addr;
388          new_var_table->size = old_var_table->size;
389          // Assign value of 0 for the missing field.
390          // Implying it is neither IMPLICIT or LINK variable as
391          // they were not supported in earlier compilers
392          new_var_table->var_alloc_type  = 0;
393          old_var_table++;
394          new_var_table++;
395       }
396       new_var_table->name = (const char *)-1;
397    }
398 
399 }
400 #endif //HOST_LIBRARY
401 
402 // Adds given list element to the global lookup table list
__offload_register_tables(FuncList::Node * entry_table,FuncList::Node * func_table,VarList::Node * var_table)403 extern "C" void __offload_register_tables(
404     FuncList::Node *entry_table,
405     FuncList::Node *func_table,
406     VarList::Node *var_table
407 )
408 {
409     OFFLOAD_DEBUG_TRACE(2, "Registering offload function entry table %p\n",
410                            entry_table);
411     __offload_entries.add_table(entry_table);
412 
413     OFFLOAD_DEBUG_TRACE(2, "Registering function table %p\n", func_table);
414     __offload_funcs.add_table(func_table);
415 
416     OFFLOAD_DEBUG_TRACE(2, "Registering var table %p\n", var_table);
417 
418     // Compiler earlier than 17.0 used a different var_table.
419     // Convert the old table to new var_table format.
420     // Only the host table for LINUX has changed.
421 #ifndef  TARGET_WINNT
422 #if HOST_LIBRARY
423     if (offload_version < OFFLOAD_VERSION_17) {
424        convert_OldVarTable_to_NewVarTable(var_table);
425     }
426 #endif
427 #endif
428     __offload_vars.add_table(var_table);
429 }
430 
431 // Removes given list element from the global lookup table list
__offload_unregister_tables(FuncList::Node * entry_table,FuncList::Node * func_table,VarList::Node * var_table)432 extern "C" void __offload_unregister_tables(
433     FuncList::Node *entry_table,
434     FuncList::Node *func_table,
435     VarList::Node *var_table
436 )
437 {
438     OFFLOAD_DEBUG_TRACE(2, "Unregistering offload function entry table %p\n",
439                            entry_table);
440     __offload_entries.remove_table(entry_table);
441 
442     OFFLOAD_DEBUG_TRACE(2, "Unregistering function table %p\n", func_table);
443     __offload_funcs.remove_table(func_table);
444 
445     OFFLOAD_DEBUG_TRACE(2, "Unregistering var table %p\n", var_table);
446 #ifndef  TARGET_WINNT
447 #if HOST_LIBRARY
448     if (offload_version < OFFLOAD_VERSION_17) {
449        // Free the malloced var_table created for backward compatiblity
450        delete var_table->table.entries;
451     }
452 #endif
453 #endif
454     __offload_vars.remove_table(var_table);
455 }
456 
457 #ifdef MYO_SUPPORT
458 
459 MYOVarTableList  __offload_myo_var_tables;
460 MYOVarTableList  __offload_myo_vtable_tables;
461 MYOFuncTableList __offload_myo_func_tables;
462 MYOInitTableList __offload_myo_init_tables;
463 
464 // Debugging dump
dump(void)465 void MYOVarTableList::dump(void)
466 {
467     OFFLOAD_DEBUG_TRACE(2, "MYO Var tables:\n");
468 
469     m_lock.lock();
470 
471     for (Node *n = m_head; n != 0; n = n->next) {
472         OFFLOAD_DEBUG_TRACE(2, "    MYO Var table:\n");
473         for (const Table::Entry *e = n->table.entries;
474              e->varName != MYO_TABLE_END_MARKER(); e++) {
475 #ifdef TARGET_WINNT
476             if (e->varName == 0) {
477                 continue;
478             }
479 #endif // TARGET_WINNT
480             OFFLOAD_DEBUG_TRACE(2, "        %s %p\n",
481                 e->varName, e->sharedAddr);
482         }
483     }
484 
485     m_lock.unlock();
486 }
487 
488 // check if any shared variables
is_empty()489 bool MYOVarTableList::is_empty()
490 {
491     OFFLOAD_DEBUG_TRACE(3, "Are MYO Var tables empty?\n");
492 
493     m_lock.lock();
494 
495     for (Node *n = m_head; n != 0; n = n->next) {
496         for (const Table::Entry *e = n->table.entries;
497              e->varName != MYO_TABLE_END_MARKER(); e++) {
498 #ifdef TARGET_WINNT
499             if (e->varName == 0) {
500                 continue;
501             }
502 #endif // TARGET_WINNT
503             m_lock.unlock();
504             OFFLOAD_DEBUG_TRACE(3, "No\n");
505             return false;
506         }
507     }
508 
509     m_lock.unlock();
510     OFFLOAD_DEBUG_TRACE(3, "Yes\n");
511     return true;
512 }
513 
dump(void)514 void MYOFuncTableList::dump(void)
515 {
516     OFFLOAD_DEBUG_TRACE(2, "MYO Func tables:\n");
517 
518     m_lock.lock();
519 
520     for (Node *n = m_head; n != 0; n = n->next) {
521         OFFLOAD_DEBUG_TRACE(2, "    MYO Func table:\n");
522         for (const Table::Entry *e = n->table.entries;
523              e->funcName != MYO_TABLE_END_MARKER(); e++) {
524 #ifdef TARGET_WINNT
525             if (e->funcName == 0) {
526                 continue;
527             }
528 #endif // TARGET_WINNT
529 #if HOST_LIBRARY
530             OFFLOAD_DEBUG_TRACE(2, "        %s %p %p\n",
531                 e->funcName, e->funcAddr, e->localThunkAddr);
532 #else // HOST_LIBRARY
533             OFFLOAD_DEBUG_TRACE(2, "        %s %p %p %p\n",
534                 e->funcName, e->funcAddr, e->wrapFuncAddr, e->localThunkAddr);
535 #endif // HOST_LIBRARY
536         }
537     }
538 
539     m_lock.unlock();
540 }
541 
542 // check if any shared functions
is_empty()543 bool MYOFuncTableList::is_empty()
544 {
545     OFFLOAD_DEBUG_TRACE(3, "Are MYO Func tables empty?\n");
546 
547     m_lock.lock();
548 
549     for (Node *n = m_head; n != 0; n = n->next) {
550         int count = 0;
551         for (const Table::Entry *e = n->table.entries;
552              e->funcName != MYO_TABLE_END_MARKER(); e++) {
553 #ifdef TARGET_WINNT
554             if (e->funcName == 0) {
555                 continue;
556             }
557 #endif // TARGET_WINNT
558             count++;
559             if (count > 1) {
560                 m_lock.unlock();
561                 OFFLOAD_DEBUG_TRACE(3, "No\n");
562                 return false;
563             }
564         }
565     }
566 
567     m_lock.unlock();
568     OFFLOAD_DEBUG_TRACE(3, "Yes\n");
569     return true;
570 }
571 
dump(void)572 void MYOInitTableList::dump(void)
573 {
574     OFFLOAD_DEBUG_TRACE(2, "MYO Init tables:\n");
575 
576     m_lock.lock();
577 
578     for (Node *n = m_head; n != 0; n = n->next) {
579         OFFLOAD_DEBUG_TRACE(2, "    MYO Init table:\n");
580         for (const Table::Entry *e = n->table.entries;
581 #ifdef TARGET_WINNT
582              e->funcName != MYO_TABLE_END_MARKER(); e++) {
583             if (e->funcName == 0) {
584                 continue;
585             }
586             OFFLOAD_DEBUG_TRACE(2, "        %s %p\n", e->funcName, e->func);
587 #else // TARGET_WINNT
588              e->func != 0; e++) {
589             OFFLOAD_DEBUG_TRACE(2, "        %p\n", e->func);
590 #endif // TARGET_WINNT
591         }
592     }
593 
594     m_lock.unlock();
595 }
596 
597 // check if any shared functions
598 bool MYOInitTableList::is_empty()
599 {
600     OFFLOAD_DEBUG_TRACE(3, "Are MYO Init tables empty?\n");
601 
602     m_lock.lock();
603 
604     for (Node *n = m_head; n != 0; n = n->next) {
605         for (const Table::Entry *e = n->table.entries;
606 #ifdef TARGET_WINNT
607              e->funcName != MYO_TABLE_END_MARKER(); e++) {
608             if (e->funcName == 0) {
609                 continue;
610             }
611             m_lock.unlock();
612             OFFLOAD_DEBUG_TRACE(3, "No\n");
613             return false;
614 #else // TARGET_WINNT
615              e->func != 0; e++) {
616 #endif // TARGET_WINNT
617         }
618     }
619 
620     m_lock.unlock();
621     OFFLOAD_DEBUG_TRACE(3, "Yes\n");
622     return true;
623 }
624 
625 extern "C" void __offload_myoRegisterTables1(
626     MYOInitTableList::Node *init_table,
627     MYOVarTableList::Node  *shared_table,
628     MYOVarTableList::Node  *shared_vtable,
629     MYOFuncTableList::Node *fptr_table
630 )
631 {
632     OFFLOAD_DEBUG_TRACE(2, "Registering MYO shared var table %p\n",
633                         shared_table);
634     __offload_myo_var_tables.add_table(shared_table);
635 
636     OFFLOAD_DEBUG_TRACE(2, "Registering MYO shared vtable table %p\n",
637                         shared_vtable);
638     __offload_myo_vtable_tables.add_table(shared_vtable);
639 
640     OFFLOAD_DEBUG_TRACE(2, "Registering MYO function table %p\n", fptr_table);
641     __offload_myo_func_tables.add_table(fptr_table);
642 
643     OFFLOAD_DEBUG_TRACE(2, "Registering MYO init table %p\n", init_table);
644     __offload_myo_init_tables.add_table(init_table);
645 }
646 
647 extern "C" void __offload_myoRemoveTables(
648     MYOInitTableList::Node *init_table,
649     MYOVarTableList::Node  *shared_table,
650     MYOVarTableList::Node  *shared_vtable,
651     MYOFuncTableList::Node *fptr_table
652 )
653 {
654     OFFLOAD_DEBUG_TRACE(3, "%s\n", __func__);
655 
656     OFFLOAD_DEBUG_TRACE(2, "Removing MYO shared var table %p\n",
657                         shared_table);
658     __offload_myo_var_tables.remove_table(shared_table);
659 
660     OFFLOAD_DEBUG_TRACE(2, "Removing MYO shared vtable table %p\n",
661                         shared_vtable);
662     __offload_myo_vtable_tables.remove_table(shared_vtable);
663 
664     OFFLOAD_DEBUG_TRACE(2, "Removing MYO function table %p\n", fptr_table);
665     __offload_myo_func_tables.remove_table(fptr_table);
666 
667     OFFLOAD_DEBUG_TRACE(2, "Removing MYO init table %p\n", init_table);
668     __offload_myo_init_tables.remove_table(init_table);
669 }
670 
671 #endif // MYO_SUPPORT
672